package org.jmap.geom;

import java.util.List;

import org.jmap.core.Extent;
import org.osgeo.proj4j.CoordinateReferenceSystem;

/**
 * interface definition to all Geometry types, all geometry types natively support
 * length calculation, area calculation, distance calculations and Projection
 * transformations
 * 
 * <ul>
 * <li>length computation with the getLength method</li>
 * <li>area computation with the getArea method</li>
 * <li>distance computation using the distanceTo method</li>
 * <li>geodesic length calculation using the getGeodesicLength method</li>
 * </ul>
 * 
 * @author Ali M. Joudeh
 *
 */
public interface Geometry {
	/**
	 * gets the parent <code>Geometry</code> object that aggregates this <code>Geometry</code>
	 * <p>if this is a root geometry, the method returns null
	 * 
	 * @return the parent geometry container, or null if not present
	 */
	Geometry getParent();
	
	/**
	 * sets a parent geometry container for this object
	 * 
	 * @param geometry
	 */
	
	void setParent(Geometry geometry);
	/**
	 * gets the bounding <code>Extent</code>
	 * 
	 * @return the bounding box that contains this <code>Geometry</code>
	 */	
	Extent getExtent();
	
	/**
	 * Extend the geometry's extent to include the new extent. 
     * If geometry's extent is not yet set, a new extent will be set
     * 
	 * @param newExtent
	 */
	void extendExtent(Extent newExtent);
	/**
	 * clears the geometry <code>Extent</code> by setting it to null,
	 * this method will clear the Parent geometry reference if non null
	 */
	void clearExtent();
	/**
	 * computes the <code>Extent</code> bounding box for this Geometry
	 */
	abstract void computeExtent();

	/**
	 * tests if another geometry object intersects with this geometry
	 * 
	 * @param geometry the geometry to test against
	 * @return boolean true if this geometry intersects the geometry passed in
	 */
	boolean intersects(Geometry geometry);
	
	/**
	 * moves/shifts the geometry by an arbitrary x and y values
	 * 
	 * @param x the x value to shift
	 * @param y the y value to shift
	 */
	public void translate(double x, double y);
	
	/**
	 * rotates the geometry around an arbitrary origin
	 * 
	 * @param angle angle to rotate in degrees
	 * @param origin center of rotation
	 */
	public void rotate(double angle, Point origin);
	
	/**
	 * scales a geometry relative to some origin uniformly
	 * 
	 * @param scaleFactor the amount of scale to perform, a value of 2 doubles the size
	 * of this geometry
	 * @param origin the origin point to use for the scale
	 * @param ratio x:y ratio for resizing
	 */
	public void scale(double scaleFactor, Point origin, double ratio);
	
	/**
	 * computes the linear distance to the another geometry object, this method returns
	 * the non-geographic distance, to find the geographic distance, use the
	 * <code>geodeticDistanceTo</code> method
	 * 
	 * @param geometry the target geometry to calculate distance to
	 * @param edge include the borders in the calculation
	 * @return the non-geographic cartesian linear distance with the target geometry
	 */
	double distanceTo(Geometry geometry, boolean edge);
	
	/**
	 * computes the geodeic distance to the target input Geometry
	 * 
	 * @param geometry the target geometry
	 * @param edge include the borders in the calculation
	 * @return the geodetic distance in meters to the target geometry
	 */
	double geodesicDistanceTo(Geometry geometry, boolean edge);

	/**
	 * gets the linear non-geographic length of the geometry
	 * 
	 * @return the non-geographic length
	 */
	double getLength();
		
	/**
	 * gets the linear geographic length of the geometry using the Vencinty Forumla
	 * 
	 * @return the geographic length in meters
	 */
	double getGeodesicLength(CoordinateReferenceSystem projection);
	
	/**
	 * gets the cartesian area for this geometry
	 * 
	 * @return the cartesian area
	 */
	double getArea();
	
	/**
	 * gets an approximate computation of a projected geometry onto the ellipsoid
	 * 
	 * @param the <code>CoordinateReferenceSystem</code> to use for computation
	 * 
	 * @return the geodetic area in square meters
	 */
	double getGeodesicArea(CoordinateReferenceSystem projection);
	
	Point getCentroid();
	
		
	/**
	 * transforms this <code>Geometry</code> from a source CoordinateReferenceSystem to a
	 * destination CoordinateReferenceSystem, this operation applies a Projection Transformation
	 * 
	 * @param sourceCRS the source <code>CoordinateReferenceSystem</code> to Project from
	 * @param targetCRS the target <code>CoordinateReferenceSystem</code> to Project to
	 * @return Projected <code>Geometry</code> from the <code>surceCRS</code> to the <code>destinationCRS</code>
	 */
	Geometry transform(CoordinateReferenceSystem sourceCRS, CoordinateReferenceSystem destinationCRS);

}
